iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
Web 3

Smart Contract Development Breakdown系列 第 7

Day 7 - Function Signature & Function Selector

  • 分享至 

  • xImage
  •  

Function Signature & Function Selector

Synchronization Link Tree


當我們從比較 Low-Level 的角度而非套件包裝好的角度看函式操作時,會發現其實我們只是送出一筆交易,其中 to:contractAddress,而 data:contractFX_encodeABI。想必 contractAddress 是非常好理解,但什麼是 contractFX_encodeABI 其實對初學者來說並不是那麼的直觀!

ABI Byte String

contractFX_encodeABI 其實就是一個 ABI Byte String,在說明他可以拿來做什麼之前,我們先來看看要怎麼得到它。假設我們在一個合約中有一個函式 myUint(),那要得到它的 ABI Byte String 有幾種方法:

  1. 在 Remix 編譯並佈署完合約之後直接按下 myUint() 那個按鈕,就可以在 console window 的 call 得到它的回傳值:
    • input field: "0x06540f7e"
  2. 在 node.js console 執行 utils 中的結果
    • $ web3.utils.sha3("myUint()").substr(0,10);
    • Return: "0x06540f7e"
  3. 使用線上 keccak-256 計算機計算 myUint() 後取前八個字元並在最前面加上 0x
    • 0x + 06540f7e
  4. 在 web3.js 或 ethers.js 等套件中直接對函式名稱做 encodeABI()
    • web3.eth.<myContract>.methods.myUint().encodeABI()

這個 Function 的完整字串實際上也就是所謂的 Function Signature,而哈希過後得到的 ABI Byte String 便是 Function Selector。

我們來看看編譯完之後產生的 Bytecode 中的 oject,會發現 06540f7e 就存在其中:

{
	"functionDebugData": {},
	"generatedSources": [],
	"linkReferences": {},
	"object": "60...06540f7e...33",
	"opcodes": "PUSH1 0x80 ... ",
	"sourceMap": "..."
}

這也就是為什麼我們在傳送交易和合約函式進行互動時,可以透過傳入 Function Signature 並 encoded 後,執行相對應的函式,因為它就藏在合約地址裡面的 Bytecode(data field) 中。


Function Signature & Function Selector

函式簽章(Function Signature)

  • 當我們要和「不需要」任何參數的 "Getter" Function 互動時,可以取函式簽章進行 keccak256 hash 後的前四個 bytes
    • function myUint()
    • bytes4(keccak256("myUint()"));
  • 當我們要和「需要」參數的 "Getter" Function 互動時,則需要把參數型態也包入進行 hash 再取前四個 bytes
    • function someFunction(uint _myUint1, address _someAddr)
    • bytes4(keccak256("someFunction(uint256,address)"))

In Smart Contract

前面介紹了如何從合約外部呼叫合約函式(交易或者前端),那我們要怎麼從合約裡面用上 Function Signature 呼叫合約函式呢?實際上和前面的做法一模一樣,也就是將 Function Signature 進行 Hash 之後取其回傳的 hex 的前 4 bytes 送給 EVM,這個結果又稱作 Function Selector。

轉換的過程如下:

contract Convert {
    /**
    * @dev This function encodes a function signature and returns it.
    * @notice There are no spaces between the parameters types, and the
    * parameters names are not included when encoding.
    * @return bytes4 : The encoded function signature -> function selector.
    */
    function getSelector(string calldata _funcSig) public(bytes4) {
        return bytes4(keccak256(bytes(_funcSig)));
    }
}

還記得我們之前提到過的 call 嗎,這邊我們一樣是使用它來呼叫函式(當然你也可以直接呼叫它的名子):

contract callFx {
    function execute(bytes _parameters) public {
        require(
            this.call(
            bytes4(keccak256("myFunc(string,uint256)")), _parameters),
            "Execution Failed"
        );
    }
}

需要注意的是 call() 這個呼叫最好要包在 require() 裡面,因為 call() 的成功與否是回傳一個 boolean 而不是彈出錯誤,所以如果沒有用 require() 包起來的話發生錯誤時我們並不會發現!

Closing

Interface

備註一下 interface,以下兩種方法是相同的:

bytes4(keccak256(“onERC721Received(address,address,uint256,bytes)”))
IERC721Receiver(0).onERC721Received.selector

DataBase

推薦大家看看這個工具,如果知道這個工具怎麼用代表你懂 Function Selector 之中的秘密了。


最後歡迎大家拍打餵食大學生0x2b83c71A59b926137D3E1f37EF20394d0495d72d


上一篇
Day 6 - Random Numbers & Bitwise
下一篇
Day 8 - Exception handling
系列文
Smart Contract Development Breakdown30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言